
#include "Defines.h"
#include "Events.h"

// common include
#include <windows.h>
#include <wrl.h>
#include <vector>

// DirectWrite drawing components.
#include <wincodec.h>
#include <dwrite_2.h>

// include d2d
#include <d2d1.h>
#include <d2d1_1.h>
#include <d2d1_1helper.h>
#include <d2d1_2.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d2d1effectauthor.h>
#include <d2d1effectauthor_1.h>
#include <d2d1effecthelpers.h>
#include <d2d1effects.h>
#include <d2d1effects_1.h>
#include <d2d1effects_2.h>
#include <d2d1helper.h>
#include <d2d1svg.h>
#include <dcommon.h>

#pragma comment(lib, "d2d1")
#pragma comment(lib, "dxguid")

// include d3d 
#include <d3d11.h>
#include <d3d11_1.h>
#include <d3d11_2.h>
#include <d3d11_3.h>
#include <d3d11_4.h>
#include <d3d11sdklayers.h>
#include <d3d11shader.h>
#include <d3d11shadertracing.h>
#include <d3dcommon.h>
#include <d3dcsx.h>

#pragma comment(lib, "d3d11")

// 引入 dxgi 接口
#include <dxgi.h>
#include <dxgi1_2.h>
#include <dxgi1_3.h>
#include <dxgi1_4.h>
#include <dxgi1_5.h>
#include <dxgi1_6.h>
#include <dxgicommon.h>
#include <dxgidebug.h>
#include <dxgiformat.h>

#pragma comment(lib, "dxgi")

#include <dwrite.h>
#pragma comment(lib, "dwrite")

// 引入 direct composition 
#include <dcomp.h>
#pragma comment(lib, "dcomp")

namespace WinDirectUI
{
	using Microsoft::WRL::ComPtr;
	typedef D2D1_SIZE_F WINDOW_SIZE_F;

#define FeatureLevelSize 7

	/// <summary>
	/// Windows 设备资源资源
	/// </summary>
	class DrawingContext
	{
		DefineEvent(DeviceLost, EventArgs)
		DefineEvent(DeviceRestored, EventArgs)

		PUBLIC DrawingContext(HWND hWnd)
		{
			DeviceLost = nullptr;
			DeviceRestored = nullptr;

			m_windowHandle = hWnd;
			m_dpi = (float)::GetDpiForWindow(hWnd);

			CreateDeviceIndependentResources();
			CreateDeviceResources();
			CreateWindowSizeDependentResources();
			CreateDirectCompositionResources();
		}

		~DrawingContext()
		{
			CoUninitialize();
		}

		PRIVATE HWND m_windowHandle;
		PRIVATE float m_dpi;

		PUBLIC float GetDpi() { return m_dpi; };

		// DirectWrite drawing components.
		Microsoft::WRL::ComPtr<IDWriteFactory2>     m_dwriteFactory;
		Microsoft::WRL::ComPtr<IWICImagingFactory2> m_wicFactory;

		PUBLIC IWICImagingFactory2* GetWicImagingFactory() const { return m_wicFactory.Get(); }
		IDWriteFactory2* GetDWriteFactory() const { return m_dwriteFactory.Get(); }

		// Direct2D objects.
		PRIVATE ComPtr<ID2D1Factory3> m_d2dFactory;
		PRIVATE ComPtr<ID2D1Device1> m_d2dDevice;
		PRIVATE ComPtr<ID2D1DeviceContext> m_d2dContext;
		PRIVATE ComPtr<ID2D1Bitmap1> m_d2dTargetBitmap;

		PUBLIC ID2D1Factory3* GetD2DFactory() const { return m_d2dFactory.Get(); }
		PUBLIC ID2D1DeviceContext* GetD2DDeviceContext() { return m_d2dContext.Get(); }

		// Direct3D objects
		PRIVATE ComPtr<ID3D11Device> m_d3d11Device;
		PRIVATE ComPtr<ID3D11DeviceContext> m_d3dContext;
		PRIVATE ComPtr<IDXGISwapChain1> m_swapChain;

		// Direct Composition objects
		PRIVATE ComPtr<IDCompositionDevice> m_compositionDevice;
		PRIVATE ComPtr<IDCompositionTarget> m_compositionTarget;
		PRIVATE ComPtr<IDCompositionVisual> m_compositionVisual;

		/// <summary>
		/// 创建设备依赖资源
		/// </summary>
		PRIVATE void CreateDeviceIndependentResources()
		{
			// Initialize Direct2D resources.
			D2D1_FACTORY_OPTIONS options;
			ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));

			ThrowIfFailed(
				::D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 
					__uuidof(ID2D1Factory3),
					&options, 
					(void**)m_d2dFactory.GetAddressOf())
			);

			// 初始化单元线程对象并发的线程
			ThrowIfFailed(CoInitializeEx(nullptr, tagCOINIT::COINIT_APARTMENTTHREADED));

			// Initialize the Windows Imaging Component (WIC) Factory.
			ThrowIfFailed(
				CoCreateInstance(CLSID_WICImagingFactory2, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_wicFactory))
			);

			// Initialize the DirectWrite Factory.
			ThrowIfFailed(
				DWriteCreateFactory(
					DWRITE_FACTORY_TYPE_SHARED,
					__uuidof(IDWriteFactory2),
					&m_dwriteFactory
				)
			);
		}

		/// <summary>
		/// 创建设备资源
		/// </summary>
		PRIVATE void CreateDeviceResources()
		{
			CreateD3DResources();
			CreateD2DResources();
		}

		/// <summary>
		/// 初始化 D3D 设备
		/// </summary>
		PRIVATE void CreateD3DResources()
		{
			D3D_FEATURE_LEVEL featureLevels[FeatureLevelSize] =
			{
				D3D_FEATURE_LEVEL_11_1,
				D3D_FEATURE_LEVEL_11_0,
				D3D_FEATURE_LEVEL_10_1,
				D3D_FEATURE_LEVEL_10_0,
				D3D_FEATURE_LEVEL_9_3,
				D3D_FEATURE_LEVEL_9_2,
				D3D_FEATURE_LEVEL_9_1
			};

			D3D_FEATURE_LEVEL featureLevelSupported;

			// https://learn.microsoft.com/zh-cn/windows/win32/api/d3d11/nf-d3d11-d3d11createdevice
			// 如果 DriverType == D3D_DRIVER_TYPE_HARDWARE，则使用的适配器将是默认适配器，
			// 这是 IDXGIFactory1：：EnumAdapters 枚举的第一个适配器
			// 如果将 pAdapter 参数设置为非 NULL 值，将 DriverType 参数设置为D3D_DRIVER_TYPE_HARDWARE值，此方法将返回E_INVALIDARG。
			// 所以 pAdapter 这里必须设置为 nullptr

			// 如果计算机上存在 Direct3D 11.1 运行时，并且 pFeatureLevels 设置为 NULL，则此函数不会创建 D3D_FEATURE_LEVEL_11_1 设备。 
			// 若要创建D3D_FEATURE_LEVEL_11_1设备，必须显式提供包含D3D_FEATURE_LEVEL_11_1的D3D_FEATURE_LEVEL数组。
			// 如果在未安装 Direct3D 11.1 运行时的计算机上提供包含D3D_FEATURE_LEVEL_11_1的D3D_FEATURE_LEVEL数组，
			// 则此函数将立即失败并E_INVALIDARG。
			ThrowIfFailed(
				::D3D11CreateDevice(
					nullptr, // pAdapter
					D3D_DRIVER_TYPE::D3D_DRIVER_TYPE_HARDWARE, // DriverType
					nullptr, // Software
					// D3D11_CREATE_DEVICE_BGRA_SUPPORT 创建支持 BGRA 格式 (DXGI_FORMAT_B8G8R8A8_UNORM 
					// 和 DXGI_FORMAT_B8G8R8A8_UNORM_SRGB) 的设备
					// D3D 设备必须指定 D3D11_CREATE_DEVICE_BGRA_SUPPORT，否则不能创建 D2D 设备
					D3D11_CREATE_DEVICE_FLAG::D3D11_CREATE_DEVICE_BGRA_SUPPORT
					| D3D11_CREATE_DEVICE_FLAG::D3D11_CREATE_DEVICE_SINGLETHREADED, // Flags
					featureLevels, // pFeatureLevels
					FeatureLevelSize, // FeatureLevels
					D3D11_SDK_VERSION, // SDKVersion
					m_d3d11Device.GetAddressOf(), // ppDevice
					&featureLevelSupported, // pFeatureLevel
					m_d3dContext.GetAddressOf()) // ppImmediateContext
			);

			if (D3D_FEATURE_LEVEL_11_1 != featureLevelSupported)
			{
				throw new InvalidOperationException("当前 windows 不支持 D3D_FEATURE_LEVEL_11_1.");
			}
		}

		/// <summary>
		/// 初始化 D2D 设备资源
		/// </summary>
		PRIVATE void CreateD2DResources()
		{
			ThrowIfFalse(m_d3d11Device != nullptr, "D3D设备未初始化.");

			ComPtr<IDXGIDevice> dxgiDevice;

			ThrowIfFailed(
				m_d3d11Device->QueryInterface(dxgiDevice.GetAddressOf())
			);

			ThrowIfFailed(
				// D3D 设备必须指定 D3D11_CREATE_DEVICE_BGRA_SUPPORT，否则不能创建 D2D 设备
				m_d2dFactory->CreateDevice(dxgiDevice.Get(), m_d2dDevice.GetAddressOf())
			);

			ThrowIfFailed(
				// 创建 d2d 上下文
				m_d2dDevice
				->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, m_d2dContext.GetAddressOf())
			);
		}

		// 创建组合设备
		PRIVATE void CreateDirectCompositionResources()
		{
			ThrowIfFalse(m_swapChain != nullptr, "交换链未初始化.");
			ThrowIfFalse(m_windowHandle != nullptr, "窗口句柄未初始化.");

			ComPtr<IDXGIDevice> dxgiDevice;
			ThrowIfFailed(
				m_d3d11Device->QueryInterface(dxgiDevice.GetAddressOf())
			);

			ThrowIfFailed(
				::DCompositionCreateDevice(dxgiDevice.Get(), IID_PPV_ARGS(m_compositionDevice.GetAddressOf()))
			);

			ThrowIfFailed(
				m_compositionDevice->CreateTargetForHwnd(m_windowHandle, true,
					m_compositionTarget.GetAddressOf())
			);

			ThrowIfFailed(
				m_compositionDevice->CreateVisual(m_compositionVisual.GetAddressOf())
			);

			// 使用交换链作为可视化对象的内容
			ThrowIfFailed(
				m_compositionVisual->SetContent(m_swapChain.Get())
			);

			ThrowIfFailed(
				m_compositionTarget->SetRoot(m_compositionVisual.Get())
			);

			// 创建完成后提交组合设备进行绘制
			ThrowIfFailed(m_compositionDevice->Commit());
		}

		/// <summary>
		/// 在 OnRender 中检查设备状态
		/// </summary>
		PUBLIC void CheckDirectCompositionDeviceState()
		{
			BOOL isValid = false;
			auto hr = this->m_compositionDevice->CheckDeviceState(&isValid);
			HandleResultException::ThrowIfFailed(hr);

			if (!isValid)
			{
				CreateNewDirectCompositionResources();
			}
		}

		/// <summary>
		/// 创建窗口尺寸依赖的资源
		/// </summary>
		PUBLIC void CreateWindowSizeDependentResources()
		{
			// Clear the previous window size specific context.
			ID3D11RenderTargetView* nullViews[] = { nullptr };
			m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr);
			m_d2dContext->SetTarget(nullptr);
			// 因为要重置尺寸，所以释放掉位图，然后重新创建一个新的尺寸的问题
			m_d2dTargetBitmap = nullptr;
			m_d3dContext->Flush();

			RECT clientRect;
			GetClientRect(m_windowHandle, &clientRect);

			if (m_swapChain != nullptr)
			{
				auto hr = m_swapChain->ResizeBuffers(
					2, // Double-buffered swap chain.
					static_cast<UINT>(RECTWIDTH(clientRect)),
					static_cast<UINT>(RECTHEIGHT(clientRect)),
					// 将此值设置为 DXGI_FORMAT_UNKNOWN 以保留后台缓冲区的现有格式
					DXGI_FORMAT_UNKNOWN,
					// 使用 GDI 进行组合呈现
					0);

				if (hr == DXGI_ERROR_DEVICE_REMOVED)
				{
					// 处理设备丢失
					HandleDeviceLost();
				}
				else {
					ThrowIfFailed(hr);
				}
			}
			else // 创建交换链
			{
				ComPtr<IDXGIFactory2> dxgiFactory;

				auto hr = ::CreateDXGIFactory2(D3D11_CREATE_DEVICE_DEBUG, IID_PPV_ARGS(dxgiFactory.GetAddressOf()));
				HandleResultException::ThrowIfFailed(hr);

				DXGI_SWAP_CHAIN_DESC1 description =
				{
					// 窗口客户区宽度放到这里~
					.Width = static_cast<UINT>(RECTWIDTH(clientRect)),
					// 窗口客户区的高度放到这里~
					.Height = static_cast<UINT>(RECTHEIGHT(clientRect)),
					// 自然的...透明的肯定是RGBA
					.Format = DXGI_FORMAT_B8G8R8A8_UNORM,
					// 我们使用的是位图模块交换链
					.SampleDesc = {
						// 设置抗锯齿的默认采样器模式的计数为 1，质量级别为 0，
						.Count = 1,
						// 因为 CreateSwapChainForComposition 模式不支持多重采样
						.Quality = 0
					},
					// 使用图面或资源作为输出呈现目标
					.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
					// 交换链中的缓冲区数
					.BufferCount = 2,
					//  IDXGIFactory2::CreateSwapChainForComposition 方法时，
					// 必须指定 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL 值，因为此方法仅支持翻转表示模型。
					.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
					// 这个模式带来的就是半透明的感觉
					.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED
				};

				ThrowIfFailed(
					dxgiFactory->CreateSwapChainForComposition(
						m_d3d11Device.Get(),
						&description,
						nullptr,
						m_swapChain.GetAddressOf())
				);
			}

			D2D1_BITMAP_PROPERTIES1 bitmapProperties =
			{
				.pixelFormat = {
					.format = DXGI_FORMAT_B8G8R8A8_UNORM,
					.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED,
				},
				.bitmapOptions = (D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW),
			};

			ComPtr<IDXGISurface2> dxgiBackBuffer;
			
			ThrowIfFailed(
				// 如果交换链的交换效果是 DXGI_SWAP_EFFECT_SEQUENTIAL 或 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL，
				// 则只能从中读取和写入交换链的零索引缓冲区
				m_swapChain->GetBuffer(0, IID_PPV_ARGS(&dxgiBackBuffer))
			);

			// 这个方法用于从一个已存在的DXGI表面（例如，来自Direct3D的纹理）创建一个Direct2D位图。
			// 这种方法的好处是它不会复制数据，而是创建了一个对原始DXGI表面的引用。
			// 这意味着当你对位图进行操作时，实际上是在操作底层的DXGI表面数据。
			// 性能影响：由于不涉及到数据复制，这种方法在性能上通常更高效，特别是在处理大型纹理或频繁更新纹理内容时。
			ThrowIfFailed(
				m_d2dContext->CreateBitmapFromDxgiSurface(dxgiBackBuffer.Get(),
					&bitmapProperties,
					m_d2dTargetBitmap.GetAddressOf())
			);

			m_d2dContext->SetTarget(m_d2dTargetBitmap.Get());

			// Grayscale text anti-aliasing is recommended for all Windows Runtime apps.
			m_d2dContext->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE);
		}

		/// <summary>
		/// 创建新的 Direct Composition 对象
		/// </summary>
		PRIVATE void CreateNewDirectCompositionResources()
		{
			m_compositionDevice = nullptr;
			m_compositionTarget = nullptr;
			m_compositionVisual = nullptr;

			CreateDirectCompositionResources();
		}

		PRIVATE void HandleDeviceLost()
		{
			// Create the new device and swap chain.
			m_swapChain = nullptr;

			if (DeviceLost != nullptr)
			{
				DeviceLost(this, EventArgs::Empty);
			}

			CreateDeviceResources();
			m_d2dContext->SetDpi(m_dpi, m_dpi);
			CreateWindowSizeDependentResources();

			// release Direct Composition objects
			CreateNewDirectCompositionResources();

			if (DeviceRestored != nullptr)
			{
				DeviceRestored(this, EventArgs::Empty);
			}
		}

		// Converts a length in device-independent pixels (DIPs) to a length in physical pixels.
		PRIVATE inline float ConvertDipsToPixels(float dips, float dpi)
		{
			static const float dipsPerInch = 96.0f;
			// 满五进一
			return floorf(dips * dpi / dipsPerInch + 0.5f); // Round to nearest integer.
		}

		// Converts a length in physical pixels to a length in device-independent pixels (DIPs).
		PRIVATE inline float ConvertPixelsToDips(float pixels)
		{
			auto dpi = GetDpiForWindow(m_windowHandle);
			static const float dipsPerInch = 96.0f;
			return pixels * dipsPerInch / dpi; // Do not round.
		}

		/// <summary>
		/// 设置窗口 dpi
		/// </summary>
		PUBLIC void SetDpi(float dpi)
		{
			if (dpi != m_dpi)
			{
				RECT windowRect;
				::GetWindowRect(m_windowHandle, &windowRect);
				m_d2dContext->SetDpi(m_dpi, m_dpi);
				CreateWindowSizeDependentResources();
			}
		}

		// Present the contents of the swap chain to the screen.
		PUBLIC void Present()
		{
			// The first argument instructs DXGI to block until VSync, putting the application
			// to sleep until the next VSync. This ensures we don't waste any cycles rendering
			// frames that will never be displayed to the screen.
			HRESULT hr = m_swapChain->Present(
				1, //  同步 1 个垂直空白之后的演示文稿
				0 // 从每个缓冲区 (显示一个帧，从当前缓冲区) 到输出
			);

			// If the device was removed either by a disconnection or a driver upgrade, we
			// must recreate all device resources.
			if (hr == DXGI_ERROR_DEVICE_REMOVED)
			{
				HandleDeviceLost();
			}
			else
			{
				ThrowIfFailed(hr);
			}

			// 创建完成后提交组合设备进行绘制
			ThrowIfFailed(m_compositionDevice->Commit());
		}
	};
}